package control.networking;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

import model.*;

/**
 * RiskServerHandler is the interface between the server and
 * clients. It manages the sockets, IP addresses, and the
 * input and output streams.
 * @author kluikens
 *
 */
public class RiskServerHandler extends Thread {

	private Socket 				clientSock;	// connection to the client
	private ObjectInputStream 	in;			// input for the server
	private ObjectOutputStream 	out;		// output for the server
	private RiskServer			masterServer;	// used for inputted serialized objects
	private Player				player;
	
	public RiskServerHandler( Socket socket, RiskServer server ) {
		clientSock = socket;
		masterServer = server;
		
		try {
			System.out.println("ServerHandler pre stream construction");
			out = new ObjectOutputStream( clientSock.getOutputStream() );
			System.out.println("ServerHandler output done");
			in = new ObjectInputStream( clientSock.getInputStream() );
			System.out.println("ServerHandler input done");
		} catch ( UnknownHostException e ) {
			e.printStackTrace();
		} catch ( IOException e ) {
			e.printStackTrace(); 
		} 
	}
	
	/**
	 * Processes the generic object received by the
	 * ObjectInputStream read and determines what instance of a class it
	 * is, and takes an accordant action based upon such.
	 * @param obj The generic object with which readObject() returns. Determines
	 * what class it is an instance of.
	 */
	private void processObject( Object obj ) {
		try {
			
			if ( obj instanceof Player ) {
				this.player = (Player) obj;
				masterServer.notifyPlayerSent();
				System.out.println("RiskServerHandler: I received Player -- " + player );
			}
			
			if ( obj instanceof Map ) {
				masterServer.updateWithNewMap( (Map) obj );
			}
			
			if (obj instanceof GameAdministration ) {
				// Are there any games to join?
				
				if ( obj instanceof JoinGame ) {
					JoinGame join = (JoinGame) obj;
					if ( masterServer.getMap() == null ) {
						join.setGameJoinable( false );
						join.setConfigInProcess( masterServer.getConfigInProcess() );
						out.writeObject( join );
						if ( masterServer.getConfigInProcess() == false )
							masterServer.setConfigInProcess( true );
							}
					
					else {						
						join.setGameJoinable( true );
						join.setConfigInProcess( masterServer.getConfigInProcess() );
						out.writeObject( join );
						}
					out.flush();
				}

				// ===================================================
				
				// Set the Game Configuration for initial setup
				if ( obj instanceof GameConfig )
					masterServer.setGameConfig( (GameConfig) obj );

				
				// ===================================================
				// Turn done? Advance the queue 
			
				if ( obj instanceof TurnDone ) {
					masterServer.advanceQueue();
				}
				
			}
			
			if (obj instanceof GameOver ) {
				//if the message of command is GameOver, then close the socket
				//clientSock.close();		// done processing, now close
			}

		} catch (IOException e) {
			e.printStackTrace();	}	
	}

	/**
	 * Broadcasts the object to the server so that the server may update
	 * the rest of the gamers with such.
	 * @param o Object instance
	 */
	public void broadcast( Object o ) {	// Writes a map to the client.
		try {
			out.writeObject( o );
			out.flush();
			
		} catch (IOException e) {
			e.printStackTrace();	}
		//System.out.println("RSH broadcast " + o);
	}
	
	/**
	 * Separated logic to read in generic objects from the ObjectInputStream.
	 */
	public void receive( ) {
		try { 
			Object o = in.readObject( );
			masterServer.updateWithNewMap( o );
			
		} catch (IOException e ) {
			e.printStackTrace();	
		} catch (ClassNotFoundException e) {
			e.printStackTrace();	}
	}
	
	/**
	 * Gets the player that this RiskServerHandler represents.
	 * @return Player instance
	 */
	public Player getPlayer( ) {
		return player;
	}
	
	/**
	 * Sets the Player instance to the argument specified.
	 * @param p
	 */
	public void setPlayer( Player p ) {
		player = p;
	}

	/**
	 * Represents the main logic of this client. It will be invoked
	 * automatically when the Thread has been started.
	 */
	public void run() {
		boolean finished = false;
		while (!finished) {
				try {
					System.out.println("RSH try to read");
//					Object toRead = in.readObject();
//					System.out.println("RSH "+toRead);
					processObject( in.readObject() );
				} catch (SocketException se) {
						System.out.println("Client disconnected");
						//TODO Remove the client from the list and replace with AI, or just delete out of game and don't replace
						try {
							clientSock.close();
							finished = true;
							// TODO Notify the ConnectedGamers and anything else of this disconnect.
						} catch (IOException e) {
							System.out.println("Error while closing disconnected client connection from the server side.");
						}
				} catch (IOException e) {
					e.printStackTrace();
				} catch (ClassNotFoundException e) {
					e.printStackTrace();		} 
		}
		
		try {
			join();
		} catch (InterruptedException e) {
			System.out.println("RiskServerHandler: Couldn't kill thread");
		}
	}
}
